import { logUnderline, logTitle, logSubTitle, log } from "../utils/index";

function use() {
    logTitle("七、高级类型");
    logSubTitle("1、交叉类型");
    // A & B & C，同时拥有ABC类型的成员；相当于&&
    function mixin<T, U>(first: T, second: U): T & U {
        const res = <T & U>{};
        // 这里只合并了属性
        for (let k in first) {
            res[k] = first[k] as any;
        }
        for (let k in second) {
            if (!(<object>res).hasOwnProperty(k)) {
                res[k] = second[k] as any;
            }
        }
        return res;
    }
    class A {
        name: string = "nameA";
    }
    interface IB {
        name: string;
        doSomeThing: (thing: string) => void;
    }
    class B implements IB {
        name: string = "nameB";
        fly: boolean = true;
        doSomeThing(thing: string): void {
            log("I do some thing");
        }
    }

    const jom = mixin(new A(), new B());
    console.log("jom: ", jom);

    logSubTitle("2、联合类型");
    // A | B | C，任其一；相当于||
    function checkType(target: string | number): boolean {
        return typeof target === "string";
    }
    log(checkType("string"));
    log(checkType(1));

    // 如果是联合类型，则只能访问其 共有成员
    class Bird {
        fly: () => void = function () {};
        eat: () => void = function () {};
    }
    class Fish {
        swim: () => void = function () {};
        eat: () => void = function () {};
    }
    function getPet(): Fish | Bird {
        return new Fish();
    }
    let pet = getPet();
    // pet.swim() // 报错，因为返回是联合类型，Fish | Bird 共有的成员只有eat
    pet.eat();

    logSubTitle("3、类型保护和区分");
    // 如果想让成功调用 pet.swim()，则需要使用断言
    if ((<Fish>pet).swim) {
        (<Fish>pet).swim();
    }
    // 或者使用 用户自定义断言: pet is Fish就是一个 类型谓词
    // 如果是判断基本类型，可以直接用 typeof，无需另写函数
    // 如果不是基本类型，可以用 instanceof，无需另写函数
    function isFish(pet: Fish | Bird): pet is Fish {
        return (<Fish>pet).swim !== undefined;
    }
    // 使用该函数，类型会自行坍缩
    if (isFish(pet)) {
        pet.swim();
    }
    // 利用 instanceof
    if (pet instanceof Fish) {
        pet.swim();
    }

    logSubTitle("4、类型别名（不推荐，尽量用接口替代）");
    // 基本用法
    type Name = string;
    type NameResolver = () => string;
    type NameOrResolver = Name | NameResolver;
    function getName(n: NameOrResolver): string {
        if (typeof n === "string") {
            return n;
        }
        return n();
    }

    // 可以使用泛型
    type DemoType<T> = { value: T };
    let a: DemoType<number>;

    // 还可以引用自己
    type Tree<T> = {
        value: T;
        left: Tree<T>;
        right: Tree<T>;
    };

    logSubTitle("5、字符串字面量类型");
    logSubTitle("6、数字字面量类型（很少用）");
    type strType = "A" | "B" | "C";
    function getResByStringType(type: strType): void {}
    getResByStringType("A");
    // getResByStringType("D") // 报错，该类型没有D

    logSubTitle("7、可辨识联合");
    // 这是一个技巧，为你的联合类型中的 公有成员 加入标识功能
    interface Square {
        type: "square";
        size: number;
    }
    interface Circle {
        type: "circle";
        radius: number;
    }
    interface Rect {
        type: "rect";
        height: number;
        width: number;
    }
    type Shape = Square | Circle | Rect;
    function getArea(s: Shape) {
        switch (s.type) {
            case "square":
                return s.size * s.size;
            case "rect":
                return s.height * s.width;
            case "circle":
                return Math.PI * s.radius ** 2;
        }
    }
    // 如果要拓展 Shape
    interface Some {
        type: "some";
    }
    type NewShape = Square | Circle | Rect | Some;
    function assertNever(x: never): never {
        throw new Error("Unexpected object: " + x);
    }
    function getNewArea(s: NewShape) {
        // 第一种办法，指定返回 number | undefined
        switch (s.type) {
            case "square":
                return s.size * s.size;
            case "rect":
                return s.height * s.width;
            case "circle":
                return Math.PI * s.radius ** 2;
            // 第二种办法，手动返回never，会在你意外传入未写case的类型时报错
            default:
                return assertNever(s);
        }
    }

    logSubTitle("8、索引类型");
    function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
        return names.map((n) => o[n]);
    }
    interface Person {
        name: string,
        age: number
    }
    let person: Person = {
        name: "lhf",
        age: 28
    }
    let strings: string[] = pluck(person, ['name'])

    logSubTitle("9、映射类型");
    type Readonly<T> = {
        readonly [P in keyof T]: T[P]
    }

    type PersonReadonly = Readonly<Person>
    let personReadonly:PersonReadonly = {
        name: "mzh",
        age: 29
    }
    // personReadonly.age = 20; // 报错，此时已经是只读属性


    logUnderline();
}

export default { use };
